///////////////////////////////////////////////////////////////////////////////
// cvec.cpp
// Author: Nick Leaf
// Last Updated: 9/2009
//
// Description: Implementation for the cvec class. See cvec.h.
///////////////////////////////////////////////////////////////////////////////

#include "cvec.h"
#include "svec.h"
#include <sstream>
#include <math.h>

//////////////////////////////////////////////////
////////// Constructors //////////////////////////
cvec::cvec(float x, float y, float z){
    v[0] = x;
    v[1] = y;
    v[2] = z;
}

cvec::cvec(float *arr){
    v[0] = arr[0];
    v[1] = arr[1];
    v[2] = arr[2];
}
//////////////////////////////////////////////////





//////////////////////////////////////////////////
////////// Accessors /////////////////////////////
float cvec::x() const {return v[0];}
float cvec::z() const {return v[2];}
float cvec::y() const {return v[1];}

float *cvec::toArray() const{
    float *v = new float[3];
    v[0] = this->v[0];
    v[1] = this->v[1];
    v[2] = this->v[2];

    return v;
}

std::string cvec::toString() const{
    std::stringstream gstring;
    gstring << *this;
    return gstring.str();
}
//////////////////////////////////////////////////





//////////////////////////////////////////////////
////////// Mutators //////////////////////////////
void  cvec::x(float x) {v[0] = x;}
void  cvec::y(float y) {v[1] = y;}
void  cvec::z(float z) {v[2] = z;}
//////////////////////////////////////////////////





//////////////////////////////////////////////////
////////// Coordinate conversion /////////////////
svec cvec::toSpherical() const{
    return svec(mag(), 
            acos(y()/mag()),
            atan2(z(), x()));
}
//////////////////////////////////////////////////





//////////////////////////////////////////////////
////////// Vector operations /////////////////////
float cvec::mag() const{
    return sqrt( pow(v[0],2.f) + pow(v[1],2.f) + pow(v[2],2.f) );
}

float cvec::dot(cvec iv) const{
    return v[0]*iv[0] + v[1]*iv[1] + v[2]*iv[2];
}

cvec cvec::plus(cvec iv) const{
    return cvec(v[0]+iv[0], v[1]+iv[1], v[2]+iv[2]);
}

cvec cvec::minus(cvec iv) const{
    return cvec(v[0]-iv[0], v[1]-iv[1], v[2]-iv[2]);
}

cvec cvec::cross(cvec iv) const{
    return cvec(v[1]*iv[2] - v[2]*iv[1],
                v[2]*iv[0] - v[0]*iv[2],
                v[0]*iv[1] - v[1]*iv[0]);
}

cvec cvec::mulByScalar(float f) const{
    return cvec(v[0]*f, v[1]*f, v[2]*f);
}

cvec cvec::divByScalar(float f) const{
    if(f!=0) return cvec(v[0]/f, v[1]/f, v[2]/f);
    else return cvec();
}

cvec cvec::norm() const{
    return divByScalar(mag());
}

float cvec::angle(cvec iv) const{
    // a*b = |a|*|b|*cos(theta)
    // acos( (a*b)/(|a|*|b|) ) = theta
    return acos( dot(iv)/(mag()*iv.mag()) );
}

cvec cvec::cwDiv(cvec iv) const{
    return cvec(v[0]/iv[0], v[1]/iv[1], v[2]/iv[2]);
}
//////////////////////////////////////////////////





//////////////////////////////////////////////////
////////// Overloaded operators //////////////////

///// value access
cvec::operator float* (){ return v; }
float &cvec::operator [] (int i){ return v[i]; }


///// vector arithmetic
cvec  cvec::operator+ (cvec iv) const{ return (*this).plus(iv); }
cvec  cvec::operator- (cvec iv) const{ return (*this).minus(iv); }
float cvec::operator* (cvec iv) const{ return (*this).dot(iv); }

cvec operator* (float f, cvec v) { return v.mulByScalar(f); }
cvec operator* (cvec v, float f) { return v.mulByScalar(f); }

cvec cvec::operator/ (float f) const{ return (*this).divByScalar(f); }


///// I/O
std::ostream& operator<< (std::ostream& out, const cvec &v){
    return out << "[" << v.x() << " " << v.y() << " " << v.z() << "]";
}

std::istream& operator>> (std::istream& in, cvec &v){
    char inc;
    
    //expects format "[x<delim>y<delim>z]"
    in >> inc >> v[0] >> inc >> v[1] >> inc >> v[2] >> inc;

    return in;
}
//////////////////////////////////////////////////
